Conflicts 😖
I hate them
You hate them
but its good to know how to resolve them.
Please always use some tools (like VSCode, jetBrains's IDEs) to resolve git conflicts. It is much easier to resolve them seeing them visually.
I will show manually so that you can understand the tools.
Conflict arises when two different change occurs in the same place in git repo.
Create a conflict with remote-git
and local-git
. To do this, please create a commit in both local-git
and remote-git
editing the same location within a file.
To accomplish this
- Use master in both repos
- change
remote-git
README.md
first line to A + 1 and commit - change
local-git
README.md
first line to A + 2 and commit pull
remote-git
intolocal-git
to create the conflict
You can resolve conflicts by choosing any combination of change.
if you resolved a conflict by taking your local changes, and if you do git status
there will be nothing, as we have taken our own changes, but is nothing changed, but we still have to perform an empty merge commit.
After the conflict has been resolved git will make a new commit for resolving conflict because it is also a change to git repo state.
And with this if you don't push your changes to remote
, and someone change remote again, then you will have perform each time an extra merge commit.
conflicts can also occur when you are trying to merge data from 1 branch to another, and they will also be resolved similarly.
Conflicts with rebase
Remember rebase
is fundamentally different from merge
. rebase
actually move our changes to the tip of the branch.
Lets create another conflict but resolve it while using rebase
instead of merge
.
Steps:
- create change in
remote-git
and inREADME.md
changeA + 1
->A + 3
. - create another change in
bar.md
LAST LINE inremote-git
- create change in
local-git
and inREADME.md
changeA + 1
->A + 4
. - create another change in
bar.md
FIRST LINE inlocal-git
- rebase
local-git
's master withremote-git
's and create the conflict
When we do this, eventually we will hit on a conflict like this:
> remote: Enumerating objects: 7, done.
remote: Counting objects: 100% (7/7), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 4 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (4/4), 337 bytes | 21.00 KiB/s, done.
From ..\remote-git\
* branch master -> FETCH_HEAD
348223b..9cf1a20 master -> origin/master
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
Auto-merging bar.md
error: could not apply 1dccd35... A+4
hint: Resolve all conflicts manually, mark them as resolved with
hint: "git add/rm <conflicted_files>", then run "git rebase --continue".
hint: You can instead skip this commit: run "git rebase --skip".
hint: To abort and get back to the state before "git rebase", run "git rebase --abort".
Could not apply 1dccd35... A+4
see here only conflict occurs in README.md
while rebase succeeded for bar.md
.
-
We can abort our rebase with
git rebase --abort
to get back to the state before, much likegit merge --abort
. -
You can instead skip this commit, run
git rebase --skip
, while performing rebase. -
After we resolved all the conflicts, we can perform
git rebase --continue
.
Now lets resolve the conflict.
We will choose remote-git
changes that means A+3
will remain after resolving conflict.
Sometime we are in a habit to commit after a resolving a conflict, but in this case we are still performing rebase
, which is playing our commits one at time.
In such case you can do
git reset --soft HEAD~1
and then continue with
git rebase --continue
So why we did all this, lets check git log
on local
git log --graph --oneline
See we do not have merge commit, with diverging branches. This is the beauty of rebase
we can keep our history straight / clean, because it is easier to debug when something bad happens.
Problem with rebase
We loose change while performing rebase.
like in above there is nowhere that A+4
change exist in our history. (😏 except in reflog
)
So this is some of the dangers of rebase. Basically we replayed our commits one by one in such a way that we removed our / theirs changes from them.
Lets try create the same issue except this time lets accept local-git
change.
- A + 5 in
remote-git
. - A + 6 in
local-git
. git pull origin trunk --rebase
inlocal-git
to cause the conflict.- accept
A + 6
change andgit rebase --continue
. - check out history to see
A + 6
commit. - Don't Push the changes.
Create a change in remote-git
and pull again.
- Add a NewLine below
A + 6
inremote-git
and commit. rebase
pullremote-git
and cause the conflict- DO NOT RESOLVE THE CONFLICT
- See the conflict
Rebase works by replaying the commits one at a time. Therefore if we have our change from a conflict and then we replay the changes we will reconflicts
on the same change again and again.
Does that mean rebase sucks? Well no
, it keeps your history very clean, but does that mean rebase can be annoying? Yes
.
To solve this issue we have rerere
rerere
stands for REuse REcorded REsolution
. Or in other words, git will automagically remember how you handled a specific conflict and will just replay your decision the next time you run into it.
to enable this we have to do
git config rerere.enabled true
Good reason to use rebase with SQUASHME
We can do a lot with rebase but I have only done rebase to squash
my multiple commits into one.
You can
- drop commits while rebasing
- change commits messages
- ...
setup your local-git
with 3 simple git commits.
- add 1 to end of read me. commit -m "added 1 ate"
- add 2 to end of read me. commit -m "added 2 ate"
- add 3 to end of read me. commit -m "added 3 ate"
Now we will use Interactive mode of rebasing and try to squash this merges into 1.
git rebase -i <commitish_value>
because of interactive mode, we get to choose how to replay our commits.
By this we can do anything with our commits.